package avicit.bdp.oss.service.auth;

import avicit.bdp.common.base.BaseService;
import avicit.bdp.common.utils.BdpLogUtil;
import avicit.bdp.common.utils.file.MinioUtils;
import avicit.bdp.oss.dao.BucketAuthDao;
import avicit.bdp.oss.dto.BucketAuthDto;
import avicit.bdp.oss.dto.BucketDto;
import avicit.bdp.oss.dto.ObjectDto;
import avicit.bdp.oss.dto.ObjectUserRelDto;
import avicit.bdp.oss.service.bucket.BucketService;
import avicit.bdp.oss.service.object.ObjectService;
import avicit.bdp.oss.utils.auth.PermissionCheckUtils;
import avicit.bdp.oss.utils.auth.StrategyEnum;
import avicit.bdp.oss.utils.common.AcitonEnum;
import avicit.bdp.oss.utils.common.CheckParaUtils;
import avicit.bdp.oss.utils.common.Constants;
import avicit.platform6.api.system.ConvertColumnClient;
import avicit.platform6.api.system.impl.SystemConstant;
import avicit.platform6.commons.utils.BusinessUtil;
import avicit.platform6.commons.utils.ComUtil;
import avicit.platform6.core.rest.msg.QueryRespBean;
import com.github.pagehelper.Page;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @金航数码科技有限责任公司
 * @作者：developer
 * @邮箱：developer@avic-digital.com
 * @创建时间：2020-11-12
 * @类说明：BucketAuthService
 * @修改记录：
 * @注意事项：
 * @功能：对象授权服务
 */
@Service
@Transactional
public class BucketAuthService extends BaseService<BucketAuthDao, BucketAuthDto> {
  @Autowired private BucketAuthDao bucketAuthDao;

  /** Object的Service层 */
  @Autowired private ObjectService objectService;

  /** Bucket的Service层 */
  @Autowired private BucketService bucketService;

  /** 对象-用户关系的Service层 */
  @Autowired private ObjectUserRelService objectUserRelService;

  /** 获取系统用户名 */
  @Autowired private ConvertColumnClient convertColumnClient;

  /**
   * 新增对象权限
   *
   * @param bucketAuthDto
   */
  public void addBucketAuth(BucketAuthDto bucketAuthDto) {
    // step1:合法性检查
    CheckParaUtils.checkObjectValidity(bucketAuthDto, logger, "新增对象授权失败，参数为null.");
    CheckParaUtils.checkStringValidity(bucketAuthDto.getObjectId(), logger, "新增对象授权失败，对象ID为null.");
    CheckParaUtils.checkStringValidity(bucketAuthDto.getAuthUser(), logger, "新增对象授权失败，授权用户为null.");
    CheckParaUtils.checkObjectValidity(
        StrategyEnum.getStrategyType(bucketAuthDto.getStrategy()),
        logger,
        "新增对象授权失败，策略非法，只能是read_only/read_write");

    // step2:获取元数据信息
    ObjectDto queryObjectRet = queryObject(bucketAuthDto.getObjectId());
    BucketDto queryBucketRet = queryBucket(queryObjectRet.getBucketName());
    checkBucketAuthExist(bucketAuthDto.getObjectId(), queryObjectRet.getObjectName());

    // step3:权限、密级检查
    PermissionCheckUtils.checkPerm(
        queryBucketRet, queryObjectRet, null, AcitonEnum.ADD_BUCKET_AUTH);

    // step4:元数据持久化
    saveAuth2Db(bucketAuthDto, queryBucketRet, queryObjectRet);
    saveObjUserRel2Db(bucketAuthDto, queryBucketRet, queryObjectRet);

    // step5:记录平台日志
    BdpLogUtil.log4Insert(bucketAuthDto);
  }

  /**
   * 修改桶授权策略
   *
   * @param bucketAuthDto
   */
  public void updateBucketAuth(BucketAuthDto bucketAuthDto) {
    // step1:合法性检查
    CheckParaUtils.checkObjectValidity(bucketAuthDto, logger, "修改对象授权失败，参数为null.");
    CheckParaUtils.checkStringValidity(bucketAuthDto.getId(), logger, "修改对象授权失败，ID为null.");
    CheckParaUtils.checkStringValidity(bucketAuthDto.getAuthUser(), logger, "修改对象授权失败，授权用户为null.");
    CheckParaUtils.checkObjectValidity(
        StrategyEnum.getStrategyType(bucketAuthDto.getStrategy()),
        logger,
        "修改对象授权失败，策略非法，只能是read_only/read_write");

    // step2:获取元数据信息
    BucketAuthDto queryBucketAuthRet = queryBucketAuth(bucketAuthDto.getId());
    ObjectDto queryObjectRet = queryObject(queryBucketAuthRet.getObjectId());
    BucketDto queryBucketRet = queryBucket(queryObjectRet.getBucketName());

    // step3:权限、密级检查
    PermissionCheckUtils.checkPerm(
        queryBucketRet, queryObjectRet, null, AcitonEnum.UPDATE_BUCKET_AUTH);

    // step4:元数据持久化
    // step4-1:删除之前的对象-用户映射关系
    delObjectUserRelDto(queryBucketAuthRet, queryBucketRet, queryObjectRet);
    // step4-2:更新对象授权元数据信息
    queryBucketAuthRet.setAuthUser(bucketAuthDto.getAuthUser());
    queryBucketAuthRet.setStrategy(bucketAuthDto.getStrategy());
    updateAuth2Db(queryBucketAuthRet);
    // step4-3:保存对象-用户映射关系
    saveObjUserRel2Db(queryBucketAuthRet, queryBucketRet, queryObjectRet);
    // step4-4:记录平台日志
    BdpLogUtil.log4Update(queryBucketAuthRet, bucketAuthDto);
  }

  /**
   * 删除桶授权策略
   *
   * @param bucketAuthDto
   */
  public void deleteBucketAuth(BucketAuthDto bucketAuthDto) {
    // step1:合法性检查
    CheckParaUtils.checkObjectValidity(bucketAuthDto, logger, "删除对象授权失败，参数为null.");
    CheckParaUtils.checkStringValidity(bucketAuthDto.getId(), logger, "删除对象授权失败，ID为null.");

    // step2:遍历id，循环删除授权
    String[] tmpIds = bucketAuthDto.getId().split(Constants.COMMA);
    for (String currId : tmpIds) {
      // step2-1:获取桶授权信息、桶信息
      BucketAuthDto queryBucketAuthRet = queryBucketAuth(bucketAuthDto.getId());
      ObjectDto queryObjectRet = queryObject(queryBucketAuthRet.getObjectId());
      BucketDto queryBucketRet = queryBucket(queryObjectRet.getBucketName());

      // step2-2:权限、密级检查
      PermissionCheckUtils.checkPerm(
          queryBucketRet, queryObjectRet, null, AcitonEnum.DELTE_BUCKET_AUTH);

      // step2-3:删除授权信息
      this.deleteByPrimaryKey(currId);
      delObjectUserRelDto(queryBucketAuthRet, queryBucketRet, queryObjectRet);

      // step2-4:记录平台日志
      BdpLogUtil.log4Delete(queryBucketAuthRet);
    }
  }

  /**
   * 查询桶授权策略
   *
   * @param sort
   * @param pageNo
   * @param pageSize
   * @return
   */
  public QueryRespBean<BucketAuthDto> getBucketAuthList(
      Integer sort, Integer pageNo, Integer pageSize, String id) {
    // step1:合法性检查
    if (sort == null) {
      sort = new Integer(1);
    }
    if (sort < Constants.ORDERED_BY_MODIFIED_TIME_DESC
        || sort > Constants.ORDERED_BY_BUCKET_NAME_DESC) {
      CheckParaUtils.printLogAndThrowException(
          logger, String.format("查询桶中授权失败，参数sort[%s]不合法", sort));
    }

    if (pageNo <= 0 || pageSize <= 0) {
      CheckParaUtils.printLogAndThrowException(
          logger, String.format("查询桶中授权失败，参数pageNo/pageSize[%s,%s]不合法", pageNo, pageSize));
    }
    CheckParaUtils.checkStringValidity(id, logger, "查询桶中授权失败, 桶ID为null");

    // step2:获取元数据信息
    BucketDto queryBucketRet = queryBucketByPrimaryKey(id);

    // step3:权限、密级检查
    PermissionCheckUtils.checkPerm(queryBucketRet, null, null, AcitonEnum.GET_BUCKET_AUTH);

    // step4:构建排序参数
    QueryRespBean<BucketAuthDto> queryRespBean = new QueryRespBean<>();
    com.github.pagehelper.PageHelper.startPage(pageNo, pageSize);
    String orderField = buildOrderField(sort);
    String orderType = buildOrderType(sort);

    // step4:查表
    Page<BucketAuthDto> bucketAuthList =
        this.bucketAuthDao.getBucketAuthList(orderField, orderType, queryBucketRet.getBucketName());
    queryRespBean.setResult(bucketAuthList);

    // step5:设置用户名
    for (BucketAuthDto bucketAuthDto : bucketAuthList.getResult()) {
      bucketAuthDto.setUserName(getUserNameByIds(bucketAuthDto.getAuthUser()));
    }

    BdpLogUtil.log4Query(queryRespBean);
    return queryRespBean;
  }

  /**
   * 对外接口；根据objectId删除权限
   *
   * @param objectDto
   */
  public void delBucketAuth(ObjectDto objectDto) {
    if (objectDto == null) {
      return;
    }

    BucketAuthDto bucketAuthDto = this.bucketAuthDao.getBucketAuthByObjectId(objectDto.getId());
    if (bucketAuthDto == null) {
      return;
    }

    delObjectUserRelDto(bucketAuthDto, null, objectDto);
    this.bucketAuthDao.deleteByPrimaryKey(bucketAuthDto.getId());
  }

  /**
   * 对外接口；查询BucketAuthDto
   *
   * @param objectId
   * @return
   */
  public BucketAuthDto queryBucketAuthByObjectId(String objectId) {
    return this.bucketAuthDao.getBucketAuthByObjectId(objectId);
  }

  /**
   * 检查对象授权是否重复
   *
   * @param objectId
   */
  private void checkBucketAuthExist(String objectId, String objectName) {
    BucketAuthDto queryBucketAuthRet = this.bucketAuthDao.getBucketAuthByObjectId(objectId);
    CheckParaUtils.checkObjectNull(
        queryBucketAuthRet, logger, String.format("%s授权重复，请修改.", objectName));
  }

  /**
   * 持久化对象授权信息
   *
   * @param bucketAuthDto
   * @param bucketDto
   * @param objectDto
   */
  private void saveAuth2Db(BucketAuthDto bucketAuthDto, BucketDto bucketDto, ObjectDto objectDto) {
    String resource = MinioUtils.buildFileName(objectDto.getPrefix(), objectDto.getObjectName());
    BucketAuthDto newAuthInfoDto =
        BucketAuthDto.builder()
            .id(ComUtil.getId())
            .bucketName(bucketDto.getBucketName())
            .objectId(objectDto.getId())
            .strategy(bucketAuthDto.getStrategy())
            .authUser(bucketAuthDto.getAuthUser())
            .resource(resource)
            .build();

    this.insert(newAuthInfoDto);
  }

  /**
   * 更新BucketAuth
   *
   * @param bucketAuthDto
   */
  private void updateAuth2Db(BucketAuthDto bucketAuthDto) {
    this.updateByPrimaryKeySelective(bucketAuthDto);
  }

  /**
   * 持久化对象-用户映射关系
   *
   * @param bucketAuthDto
   * @param bucketDto
   * @param objectDto
   */
  private void saveObjUserRel2Db(
      BucketAuthDto bucketAuthDto, BucketDto bucketDto, ObjectDto objectDto) {
    List<ObjectDto> queryObjectList =
        getObjects(objectDto.getBucketName(), objectDto.getPrefix(), objectDto.getObjectName());

    for (ObjectDto tmpObjectDto : queryObjectList) {
      ObjectUserRelDto objectUserRelDto =
          ObjectUserRelDto.builder()
              .bucketId(bucketDto.getId())
              .objectId(tmpObjectDto.getId())
              .bucketName(tmpObjectDto.getBucketName())
              .prefix(tmpObjectDto.getPrefix())
              .objectName(tmpObjectDto.getObjectName())
              .build();
      saveObjUserRel2DbInner(bucketAuthDto.getAuthUser(), objectUserRelDto);
    }
  }

  /**
   * 拆分userId，每一个userId都记录一次
   *
   * @param userId
   * @param objectUserRelDto
   */
  private void saveObjUserRel2DbInner(String userId, ObjectUserRelDto objectUserRelDto) {
    String[] tmpStrs = userId.trim().split(Constants.COMMA);
    for (String id : tmpStrs) {
      objectUserRelDto.setUserId(id);
      objectUserRelDto.setId(ComUtil.getId());
      this.objectUserRelService.insert(objectUserRelDto);
    }
  }

  /**
   * 删除对象-用户映射关系
   *
   * @param bucketAuthDto
   * @param bucketDto
   * @param objectDto
   */
  private void delObjectUserRelDto(
      BucketAuthDto bucketAuthDto, BucketDto bucketDto, ObjectDto objectDto) {
    Map<String, String> map =
        parsePath(MinioUtils.buildFileName(objectDto.getPrefix(), objectDto.getObjectName()));
    for (Map.Entry<String, String> entry : map.entrySet()) {
      // Map中“”标识为null，所以对entry.getKey()特殊处理
      delObjectUserRelInner(
          objectDto.getBucketName(),
          entry.getKey() == null ? Constants.BLACK : entry.getKey(),
          entry.getValue(),
          bucketAuthDto.getAuthUser());
    }
  }

  /**
   * 删除对象-用户映射关系
   *
   * @param bucketName
   * @param prefix
   * @param objectName
   * @param userId
   */
  private void delObjectUserRelInner(
      String bucketName, String prefix, String objectName, String userId) {
    String[] tmpStrs = userId.trim().split(Constants.COMMA);
    for (String id : tmpStrs) {
      this.objectUserRelService.delObjectUserRelDto(bucketName, prefix, objectName, id);
    }
  }

  /**
   * 解析路径，获取完整路径上所有对象元数据
   *
   * @param bucketName
   * @param prefix
   * @param objectName
   * @return
   */
  private List<ObjectDto> getObjects(String bucketName, String prefix, String objectName) {
    List<ObjectDto> objectDtoList = new ArrayList<>();

    Map<String, String> map = parsePath(MinioUtils.buildFileName(prefix, objectName));
    for (Map.Entry<String, String> entry : map.entrySet()) {
      ObjectDto queryRet =
          this.objectService.getObject(bucketName, entry.getKey(), entry.getValue());
      objectDtoList.add(queryRet);
    }

    return objectDtoList;
  }

  /**
   * 构建OrderField
   *
   * @param sort
   * @return
   */
  private String buildOrderField(Integer sort) {
    // 排序，1:按修改时间倒序、2:按修改时间升序、3:按桶名称升序、4:按桶名称倒序
    String orderField = Constants.ORDERED_BY_LAST_UPDATE_TIME;
    if (sort != null) {
      if (Constants.ORDERED_BY_MODIFIED_TIME_DESC == sort) {
        orderField = Constants.ORDERED_BY_LAST_UPDATE_TIME;
      } else if (Constants.ORDERED_BY_MODIFIED_TIME_ASC == sort) {
        orderField = Constants.ORDERED_BY_LAST_UPDATE_TIME;
      } else {
        orderField = Constants.ORDERED_BY_LAST_UPDATE_TIME;
      }
    }

    return orderField;
  }

  /**
   * 构建OrderType
   *
   * @param sort
   * @return
   */
  private String buildOrderType(Integer sort) {
    // 排序，1:按修改时间倒序、2:按修改时间升序、3:按桶名称升序、4:按桶名称倒序
    String orderType = Constants.ORDERED_DESC;
    if (sort != null) {
      if (Constants.ORDERED_BY_MODIFIED_TIME_DESC == sort) {
        orderType = Constants.ORDERED_DESC;
      } else if (Constants.ORDERED_BY_MODIFIED_TIME_ASC == sort) {
        orderType = Constants.ORDERED_ASC;
      } else {
        orderType = Constants.ORDERED_DESC;
      }
    }

    return orderType;
  }

  /**
   * 解析路径，获取各个层级对象名称 示例：dir1/dir2/file3，返回[dir1,dir2,file3]
   *
   * @param path
   * @return
   */
  private Map<String, String> parsePath(String path) {
    Map<String, String> map = new HashMap<>();

    if (path == null) {
      return map;
    }

    StringBuilder prefix = new StringBuilder();
    String[] tmpStrs = path.trim().split(Constants.SLASH);
    for (int i = 0; i < tmpStrs.length; i++) {
      if (i == 0) {
        map.put(Constants.BLACK, tmpStrs[i]);
        prefix.append(tmpStrs[i]);
      } else {
        map.put(prefix.toString(), tmpStrs[i]);
        prefix.append(Constants.SLASH).append(tmpStrs[i]);
      }
    }

    return map;
  }

  /**
   * 查询Object
   *
   * @param objectId
   * @return
   */
  private ObjectDto queryObject(String objectId) {
    ObjectDto queryObjectRet = this.objectService.queryObjectDto(objectId, true);
    CheckParaUtils.checkObjectValidity(
        queryObjectRet, logger, String.format("对象[%s]不存在.", objectId));

    return queryObjectRet;
  }

  /**
   * 查询Bucket
   *
   * @param bucketName
   * @return
   */
  private BucketDto queryBucket(String bucketName) {
    BucketDto queryBucketRet = this.bucketService.getBucketByName(bucketName);
    CheckParaUtils.checkObjectValidity(
        queryBucketRet, logger, String.format("桶[%s]不存在.", bucketName));

    return queryBucketRet;
  }

  /**
   * 查询Bucket
   *
   * @param id
   * @return
   */
  private BucketDto queryBucketByPrimaryKey(String id) {
    BucketDto queryBucketRet = this.bucketService.selectByPrimaryKey(id);
    CheckParaUtils.checkObjectValidity(
        queryBucketRet, logger, String.format("查询桶中授权失败, 桶[%s]不存在", id));

    return queryBucketRet;
  }

  /**
   * 查询BucketAuthDto
   *
   * @param id
   * @return
   */
  private BucketAuthDto queryBucketAuth(String id) {
    BucketAuthDto queryBucketAuthDto = this.selectByPrimaryKey(id);
    CheckParaUtils.checkObjectValidity(
        queryBucketAuthDto, logger, String.format("对象授权信息[%s]不存在.", id));

    return queryBucketAuthDto;
  }

  /**
   * 查询ObjectUserRelDto
   *
   * @param bucketName
   * @param prefix
   * @param objectName
   * @param userId
   * @return
   */
  private ObjectUserRelDto queryObjUsrRel(
      String bucketName, String prefix, String objectName, String userId) {
    ObjectUserRelDto queryObjUsrRelDto =
        this.objectUserRelService.queryObjectUserRelDto(bucketName, prefix, objectName, userId);
    CheckParaUtils.checkObjectValidity(
        queryObjUsrRelDto,
        logger,
        String.format(
            "对象-用户映射关系不存在, bucket=%s,prefix=%s,object=%s,userId=%s.",
            bucketName, prefix, objectName, userId));

    return queryObjUsrRelDto;
  }

  /**
   * 根据用户ID获取用户名
   *
   * @param userIds
   * @return
   * @throws Exception
   */
  private String getUserNameByIds(String userIds) {
    if (StringUtils.isEmpty(userIds)) {
      return null;
    }

    Map<String, Set<String>> convertFormData = new HashMap<>();
    String[] array = userIds.trim().split(Constants.COMMA);
    for (String userId : array) {
      BusinessUtil.createConvertSet(convertFormData, SystemConstant.USER, userId);
    }

    StringBuffer buf = new StringBuffer(Constants.BLACK);
    if (convertFormData.size() > 0) {
      // 获取请求结果
      Map<String, Map<String, String>> convertResultData =
          convertColumnClient.replace(convertFormData);
      for (String userId : array) {
        String name = BusinessUtil.convertFormat(convertResultData, SystemConstant.USER, userId);
        buf.append(name).append(Constants.COMMA);
      }
    }

    String names = buf.toString();
    if (names.length() >= 1) {
      names = names.substring(0, names.length() - 1);
    }
    return names;
  }
}
